﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Kernel;
using ElectionOfLeaderAlg.Controllers;

namespace ElectionOfLiderAlg
{
    public class ElectionRingChenRoberts : IStepController
    {
        private readonly Graph _graph;
        private int _step = 0;

        /// <summary>
        /// Создаёт контроллер для алгоритма Ле-Лана на кольце
        /// </summary>
        /// <param name="nodeCount">общее количество вершин</param>
        /// <param name="initiatorCount">количество инициаторов</param>
        public ElectionRingChenRoberts(int nodeCount, int countInit, RichTextBox rtbLog)
        {
            if (countInit > nodeCount)
                throw new Exception("Инициаторов не может быть больше чем вершин в графе");
            _graph = GraphGenerator.CreateRing(nodeCount);
            if (countInit == nodeCount)
            {
                _graph.Nodes.ForEach(x => x.IsInitiator = true);
            }
            else
            {
                Random rand = new Random();
                int colI = 0;
                int randIn = 0;
                while (colI != countInit)
                {
                    randIn = rand.Next(0, nodeCount - 1);
                    if (!_graph.Nodes[randIn].IsInitiator)
                    {
                        colI++;
                        _graph.Nodes[randIn].IsInitiator = true;
                    }
                }
            }
            _graph.Nodes.ForEach(x =>
                {
                    x.rtbLog = rtbLog;
                    if (x.IsInitiator) x.State = x.State = GraphNode.NodeState.Cand;
                });
        }

        public Graph NextStep()
        {
            //принимаем все сообщения, отправленные на предыдущем шаге
            _graph.Nodes.ForEach(x => x.ReceiveMessages());

            if (_step == 0)
                _graph.Nodes.Where(x => x.IsInitiator).ToList().ForEach(y =>
                    {
                        var nextNode = y.Children.Single();
                        y.SendMessage(nextNode, new Msg(y.Number, MessageType.tok));
                    });
            
            foreach (var node in _graph.Nodes)
            {
                if (node.IsInitiator)
                {
                    if (node.State != GraphNode.NodeState.Leader)
                    {
                        if (node.ReceivedMessages.Any(x => x.number == node.Number))
                        {
                            node.State = GraphNode.NodeState.Leader;
                        }
                        else
                            if (node.ReceivedMessages.Count != 0)
                            {
                                Msg tmp = node.ReceivedMessages.Single();
                                if (tmp.number < node.Number)
                                {
                                    if (node.State == GraphNode.NodeState.Cand)
                                        node.State = GraphNode.NodeState.Lost;
                                    var nextNode = node.Children.Single();
                                    node.SendMessage(nextNode, tmp);
                                }
                            }
                    }
                }
                else
                {
                    if (node.State == GraphNode.NodeState.Sleep)
                        node.State = GraphNode.NodeState.Lost;
                    if (node.ReceivedMessages.Count != 0)
                    {
                        Msg tmp = node.ReceivedMessages.Dequeue();
                        var nextNode = node.Children.Single();
                        node.SendMessage(nextNode, tmp);
                    }
                }
            }

            _step++;

            return _graph;
        }

        public bool IsEnd()
        {
            return _graph.Nodes.Any(x => x.State == GraphNode.NodeState.Leader);
        }

        public Graph GetCurrentState()
        {
            return _graph; 
        }
    }
}
